package filter

import (
	"file_explorer/model"
	"file_explorer/service/eventbus"
	"file_explorer/view/packed_widgets"
	"file_explorer/view/store"

	"fyne.io/fyne/v2"
	"fyne.io/fyne/v2/container"
	"fyne.io/fyne/v2/widget"
)

const (
	RelationAnd = "and"
	RelationOr  = "or"
)

type FileTagFilter struct {
	content *fyne.Container
	tagList *fyne.Container

	checklist *packed_widgets.Checklist

	relation string
	tags     []string

	tabCtx   *store.FileTabContext
	onChange func()
}

func (filter *FileTagFilter) GetCanvasObject() fyne.CanvasObject {
	return filter.content
}

func (filter *FileTagFilter) GetValue() string {
	return ""
}

func (filter *FileTagFilter) LoadValue(val string) {
}

func (filter *FileTagFilter) Reset() {
	filter.refreshTagList()
}

// 或关系标签过滤，文件含任意一个被选标签时通过
func (filter *FileTagFilter) filterByOrRelation(info *model.FileInfo, checkTags []string) bool {
	if len(info.GetTagList()) == 0 {
		return false
	}

	// 判断文件标签与被选标签存在交集
	list1 := info.GetTagList()
	list2 := checkTags
	if len(list2) < len(list1) {
		list1, list2 = list2, list1
	}

	tagSet := make(map[string]struct{})
	for _, tag := range list1 {
		tagSet[tag] = struct{}{}
	}

	for _, tag := range list2 {
		if _, ok := tagSet[tag]; ok {
			return true
		}
	}

	return false
}

// 与关系标签过滤，文件含所有被选标签时通过
func (filter *FileTagFilter) filterByAndRelation(info *model.FileInfo, checkTags []string) bool {
	// 文件标签数<被选中标签数
	if len(info.GetTagList()) < len(checkTags) {
		return false
	}

	// 判断被选标签内是否包含文件标签内不存在的标签
	tagSet := make(map[string]struct{})
	for _, tag := range info.GetTagList() {
		tagSet[tag] = struct{}{}
	}

	for _, tag := range checkTags {
		if _, ok := tagSet[tag]; !ok {
			return false
		}
	}

	return true
}

func (filter *FileTagFilter) Filter(info *model.FileInfo) bool {
	if filter.checklist == nil {
		return true
	}
	checkIndexList := filter.checklist.GetCheckIndexList()

	// 未选中任何标签，显示全部文件
	if len(checkIndexList) == 0 {
		return true
	}

	checkTags := make([]string, len(checkIndexList))
	for i, checkIndex := range checkIndexList {
		checkTags[i] = filter.tags[checkIndex]
	}

	if filter.relation == RelationOr {
		return filter.filterByOrRelation(info, checkTags)
	}

	return filter.filterByAndRelation(info, checkTags)
}

func (filter *FileTagFilter) buildCtrlOption() *fyne.Container {
	checkAll := widget.NewCheck("全选", func(b bool) {
		if filter.checklist == nil {
			return
		}
		filter.checklist.CheckAll(b)
	})

	radioGroup := widget.NewRadioGroup([]string{RelationOr, RelationAnd}, func(s string) {
		filter.onChange()
	})
	radioGroup.Horizontal = true
	radioGroup.SetSelected(RelationOr)

	return container.NewHBox(checkAll, widget.NewSeparator(), radioGroup)
}

func (filter *FileTagFilter) refreshTagList() *fyne.Container {
	filter.tags = model.BatchGetTagList(filter.tabCtx.FileInfos)
	filter.checklist.Reset(len(filter.tags))
	filter.checklist.RegisterConsumer(eventbus.EventKeyWidgetChecklistChange, func(event *model.Event) {
		filter.onChange()
	})

	// 刷新tagList
	filter.tagList.RemoveAll()
	for i, tag := range filter.tags {
		checkBox := widget.NewCheck(tag, nil)
		checkBox.OnChanged = func(b bool) {
			filter.checklist.Check(i, b)
		}
		filter.tagList.Add(checkBox)
	}
	filter.tagList.Refresh()

	return filter.tagList
}

func NewFileTagFilter(tabCtx *store.FileTabContext, onChange func()) *FileTagFilter {
	filter := &FileTagFilter{
		tabCtx:    tabCtx,
		onChange:  onChange,
		tagList:   container.NewVBox(),
		content:   container.NewVBox(),
		checklist: packed_widgets.NewChecklist(),
	}

	filter.content.Add(filter.buildCtrlOption())
	filter.content.Add(filter.refreshTagList())

	return filter
}
